package databases;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;

import objects.Measurement;
import settings.Settings;
import utilities.ANSI;
import utilities.Console;
import utilities.DateTool;
import utilities.FormatDouble;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.MongoException;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;

/**
 * The Class MongoDB.
 * 
 * @author Nils Berlijn
 * @author Henderikus Harms
 * @version 1.0
 * @since June 1, 2014
 */
public final class MongoDB 
{
	/** The mongo client. */
	private static MongoClient mongoClient;
	
	/** The database. */
	private static DB database;
	
	/** The collection. */
	private static DBCollection collection;
	
	/** The console. */
	private static Console console;

	/**
	 * Connect.
	 */
	public static void connect()
	{
		try 
		{
			mongoClient = new MongoClient(Arrays.asList(
					new ServerAddress(Settings.MONGODB_ADDRESS, Settings.MONGODB_ADDRESS_PORT1),
					new ServerAddress(Settings.MONGODB_ADDRESS, Settings.MONGODB_ADDRESS_PORT2),
					new ServerAddress(Settings.MONGODB_ADDRESS, Settings.MONGODB_ADDRESS_PORT3)));
			
			mongoClient.setReadPreference(ReadPreference.secondaryPreferred());

			database = mongoClient.getDB(Settings.MONGODB_DATABASE);
			collection = database.getCollection(Settings.MONGODB_COLLECTION);

			System.out.println(ANSI.GREEN + "MongoDB connection succesfull established." + ANSI.NORMAL);	
			System.out.println(ANSI.YELLOW + "MongoDB is waiting for incoming queries...\n" + ANSI.NORMAL);				
		} 
		catch(Exception e) 
		{
			System.out.println(ANSI.RED + "MongoDB connection failed by trying to establishing a new connection.\n" + ANSI.NORMAL);	
		}
	}
	
	/**
	 * Console.
	 *
	 * @param console the console
	 */
	public static void console(Console console)
	{
		MongoDB.console = console;
	}
	
	/**
	 * Prints the object.
	 *
	 * @param object the object
	 */
	public static void printObject(DBObject object)
	{
		console.write(ANSI.YELLOW + " _________________________\n");
		console.write(" STN:\t\t" + object.get("STN"));
		console.write(" DATE:\t\t" + object.get("DATE"));
		console.write(" TIME:\t\t" + object.get("TIME"));
		console.write(" TEMP:\t\t" + object.get("TEMP"));
		console.write(" DEWP:\t\t" + object.get("DEWP"));
		console.write(" STP:\t\t" + object.get("STP"));
		console.write(" SLP:\t\t" + object.get("SLP"));
		console.write(" VISIB:\t\t" + object.get("VISIB"));
		console.write(" WDSP:\t\t" + object.get("WDSP"));
		console.write(" PRCP:\t\t" + object.get("PRCP"));
		console.write(" SNDP:\t\t" + object.get("SNDP"));
		console.write(" FRSHTT:\t" + object.get("FRSHTT"));
		console.write(" CLDC:\t\t" + object.get("CLDC"));
		console.write(" WNDDIR:\t" + object.get("WNDDIR"));	
		console.write(" _________________________" + ANSI.NORMAL);			
	}	
	
	/**
	 * Insert measurement.
	 *
	 * @param measurement the measurement
	 * @throws MongoException the mongo exception
	 */
	public static void insertMeasurement(Measurement measurement) throws MongoException
	{
		BasicDBObject object = new BasicDBObject("STN", measurement.getStn())
			.append("DATE", measurement.getDate())
			.append("TIME", measurement.getTime())
			.append("TEMP", measurement.getTemp())
			.append("DEWP", measurement.getDewp())
			.append("STP", measurement.getStp())
			.append("SLP", measurement.getSlp())
			.append("VISIB", measurement.getVisib())
			.append("WDSP", measurement.getWdsp())
			.append("PRCP", measurement.getPrcp())
			.append("SNDP", measurement.getSndp())
			.append("FRSHTT", measurement.getFrshtt())
			.append("CLDC", measurement.getCldc())
			.append("WNDDIR", measurement.getWnddir());
	
		collection.insert(object);	
	}

	/**
	 * Select integer avarage latest measurements key by stn and key.
	 *
	 * @param stn the stn
	 * @param key the key
	 * @param amount the amount
	 * @return the string
	 */
	public static synchronized String selectIntegerAvarageLatestMeasurementsKeyByStnAndKey(int stn, String key, int amount)
	{
		BasicDBObject query = new BasicDBObject();	
			query.put("STN", stn);
		
		DBCursor cursor = collection.find(query).sort(new BasicDBObject("_id", -1)).limit(amount);

		Integer sum = 0;
		int divide = 1;
		
		try 
		{
			while(cursor.hasNext()) 
			{
				DBObject object = cursor.next();
				
				sum += Integer.parseInt(object.get(key).toString());
			}
		} 
		finally 
		{
		   cursor.close();
		}
		
		sum = sum / divide;		
		String avarage = sum.toString();

		return avarage;
	}	
	
	/**
	 * Select double avarage latest measurements key by stn and key.
	 *
	 * @param stn the stn
	 * @param key the key
	 * @param decimals the decimals
	 * @param amount the amount
	 * @return the string
	 */
	public static synchronized String selectDoubleAvarageLatestMeasurementsKeyByStnAndKey(int stn, String key, int decimals, int amount)
	{
		BasicDBObject query = new BasicDBObject();	
			query.put("STN", stn);
		
		DBCursor cursor = collection.find(query).sort(new BasicDBObject("_id", -1)).limit(amount);

		Double sum = 0.0;
		int divide = 1;

		try 
		{
			while(cursor.hasNext()) 
			{
				DBObject object = cursor.next();
				sum += Double.parseDouble(object.get(key).toString());
				divide++;
			}
		} 
		finally 
		{
		   cursor.close();
		}
		
		sum = sum / divide;
		String avarage = FormatDouble.format(sum, decimals);

		return avarage;
	}		
	
	/**
	 * Select measurements by stn.
	 *
	 * @param stn the stn
	 * @param amount the amount
	 * @throws MongoException the mongo exception
	 */
	public static void selectMeasurementsByStn(int stn, int amount) throws MongoException
	{
		BasicDBObject query = new BasicDBObject();	
			query.put("STN", stn);

		DBCursor cursor = collection.find(query).limit(amount);

		try 
		{
			while(cursor.hasNext()) 
			{
				DBObject object = cursor.next();
				
				printObject(object);				
			}
		} 
		finally 
		{
		   cursor.close();
		}
	}		

	/**
	 * Select measurements by country.
	 *
	 * @param country the country
	 * @param amountWeatherStations the amount weather stations
	 * @param amountMeasurements the amount measurements
	 * @throws MongoException the mongo exception
	 */
	public static void selectMeasurementsByCountry(String country, int amountWeatherStations, int amountMeasurements) throws MongoException
	{
		try
		{
			ResultSet resultSet = MySQL.selectWeatherStationStnByCountry(country, amountWeatherStations);	
			
			while(resultSet.next())
			{
				selectMeasurementsByStn(resultSet.getInt("stn"), amountMeasurements);		
			}
		}
		catch(SQLException e)
		{
			
		}
	}
	
	/**
	 * Select measurements last day by country.
	 *
	 * @param country the country
	 * @param amountWeatherStations the amount weather stations
	 * @param amountMeasurements the amount measurements
	 * @throws MongoException the mongo exception
	 */
	public static void selectMeasurementsLastDayByCountry(String country, int amountWeatherStations, int amountMeasurements) throws MongoException
	{
		try
		{
			ResultSet resultSet = MySQL.selectWeatherStationStnByCountry(country, amountWeatherStations);	

			while(resultSet.next())
			{
				BasicDBObject query = new BasicDBObject("STN", resultSet.getInt("stn"));
					query.put("DATE", DateTool.date(0));				
					query.put("DATE", DateTool.date(-1));

				DBCursor cursor = collection.find(query).limit(amountMeasurements);				

				try 
				{
					while(cursor.hasNext()) 
					{
						DBObject object = cursor.next();
						
						printObject(object);				
					}
				} 
				finally 
				{
				   cursor.close();
				}	
			}
		}
		catch(SQLException e)
		{
			
		}
	}
	
	/**
	 * Select first measurement.
	 *
	 * @throws MongoException the mongo exception
	 */
	public static void selectFirstMeasurement() throws MongoException
	{
		DBObject object = collection.findOne();
		
		printObject(object);
	}
	
	/**
	 * Select latest measurement.
	 *
	 * @throws MongoException the mongo exception
	 */
	public static void selectLatestMeasurement() throws MongoException
	{
		BasicDBObject query = new BasicDBObject();		
		DBCursor cursor = collection.find(query).sort(new BasicDBObject("_id", -1)).limit(1);
		
		try 
		{
			while(cursor.hasNext()) 
			{
				DBObject object = cursor.next();
				
				printObject(object);
			}
		} 
		finally 
		{
		   cursor.close();
		}
	}		
}
